package cn.jsu.oj.dispatchServer.satellite.netty;

import cn.jsu.oj.dispatchServer.satellite.config.ResourceConfig;
import cn.jsu.oj.dispatchServer.satellite.factory.Factory;
import cn.jsu.oj.dispatchServer.satellite.netty.starter.SatelliteStarter;
import cn.jsu.oj.dispatchServer.satellite.pojo.net.AbsPackNet;
import cn.jsu.oj.dispatchServer.satellite.util.NetUtil;
import cn.jsu.oj.dispatchServer.satellite.util.SpringUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * 入口点处理器
 *
 * @author shan
 * @date 2022/06/29
 */
@Slf4j
public class EntryPointHandler extends ByteToMessageDecoder {

    Factory packNoFactory = (Factory) SpringUtil.getBean("packNoFactory");

    /**
     * 包头
     */
    private static final String PACK_HEAD = ResourceConfig.getConfigString("client-pack-head");

    /**
     * 包基本长度
     */
    private static final Integer PACK_BASE_LENGTH = ResourceConfig.getConfigInteger("client-pack-base-length");

    /**
     * 包编码格式
     */
    private static final String PACK_CHARSET = ResourceConfig.getConfigString("client-pack-charset");

    /**
     * 解码
     *
     * @param ctx ctx
     * @param in  输入
     * @param out 输出
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        if (in.readableBytes() < PACK_BASE_LENGTH) {
            return;
        }
        System.out.println("\n");
//        log.info("----------数据入站......");
        int originCursor = in.readerIndex();
        if (packHeadVerify(in)) {
            int length = in.readInt();
            if (in.readableBytes() < length) {
                in.readerIndex(originCursor);
                return;
            }
            if (!packOriginVerify(ctx, in)) {
                return;
            }
            AbsPackNet absPackNet = payloadParse(in, length);
//            log.info("----------解析完成......");
            try {
                log.info("----------类型: " + absPackNet.getClass().getName() + "......");
                out.add(absPackNet);
            } catch (Exception e) {
                e.printStackTrace();
                log.error(e.getMessage());
            }
        }
    }

    /**
     * 包头验证
     *
     * @param in 输入
     * @return {@link Boolean }
     */
    private Boolean packHeadVerify(ByteBuf in) {
//        log.info("----------开始包头验证......");
        while (true) {
            in.markReaderIndex();
            int headLength = in.readInt();
            if (headLength > 0 && headLength < 10) {
                byte[] packHead = new byte[headLength];
                in.readBytes(packHead);
                try {
                    if (PACK_HEAD.equals(new String(packHead, PACK_CHARSET))) {
//                        log.info("----------验证成功......");
                        break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error(e.getMessage());
                }
            }
            in.resetReaderIndex();
            in.readByte();
            if (in.readableBytes() < PACK_BASE_LENGTH) {
                log.warn("----------验证失败......");
                return false;
            }
        }
        return true;
    }

    /**
     * 包源检验
     *
     * @param ctx ctx
     * @return {@link Boolean }
     */
    private Boolean packOriginVerify(ChannelHandlerContext ctx, ByteBuf in) {
        in.markReaderIndex();
        short packNo = in.readShort();
        String identity = NetUtil.getIdentify(ctx.channel());
        if (!SatelliteStarter.activeSign || !SatelliteStarter.verifySign) {
            if (packNo != 2) {
                log.warn(identity + ": 危险数据源，已拦截......");
                return false;
            }
        }
        if (!(Boolean) packNoFactory.getProduct(packNo)) {
            log.warn(identity + ": 未遵循协议，已拦截......");
            return false;
        }
        in.resetReaderIndex();
        return true;
    }

    /**
     * 载荷解析
     *
     * @param in     输入
     * @param length 长度
     * @return {@link AbsPackNet }
     */
    private AbsPackNet payloadParse(ByteBuf in, Integer length) {
//        log.info("----------开始负载解析......");
        byte[] payload = new byte[length];
        in.readBytes(payload);
        AbsPackNet absPackNet = null;
        try {
            absPackNet = AbsPackNet.deserialize(payload);
//            log.info("----------解析成功......");
//            log.info("----------类型: " + absPackNet.getClass().getName() + "......");
        } catch (Exception e) {
            e.printStackTrace();
            log.info("--------解析失败......");
        }
        return absPackNet;
    }
}